﻿using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;

namespace Microsoft.Xna.Framework.Input
{
    /// <summary>
    /// Allows retrieval of keystrokes from a keyboard input device.
    /// </summary>
    public static class Keyboard
    {
		static Dictionary<Key, Keys> mappedKeys = new Dictionary<Key, Keys>(new KeyComparer());
		static Dictionary<int, Keys> platformSpecificKeys = new Dictionary<int, Keys>();
        static KeyboardState state = new KeyboardState();
		static List<Keys> pressedKeys = new List<Keys>();

		static Keyboard()
		{
			mappedKeys.Add(System.Windows.Input.Key.A, Keys.A);
			mappedKeys.Add(System.Windows.Input.Key.Add, Keys.Add);
			mappedKeys.Add(System.Windows.Input.Key.B, Keys.B);
			mappedKeys.Add(System.Windows.Input.Key.Back, Keys.Back);
			mappedKeys.Add(System.Windows.Input.Key.C, Keys.C);
			mappedKeys.Add(System.Windows.Input.Key.CapsLock, Keys.CapsLock);
			mappedKeys.Add(System.Windows.Input.Key.D, Keys.D);
			mappedKeys.Add(System.Windows.Input.Key.D0, Keys.D0);
			mappedKeys.Add(System.Windows.Input.Key.D1, Keys.D1);
			mappedKeys.Add(System.Windows.Input.Key.D2, Keys.D2);
			mappedKeys.Add(System.Windows.Input.Key.D3, Keys.D3);
			mappedKeys.Add(System.Windows.Input.Key.D4, Keys.D4);
			mappedKeys.Add(System.Windows.Input.Key.D5, Keys.D5);
			mappedKeys.Add(System.Windows.Input.Key.D6, Keys.D6);
			mappedKeys.Add(System.Windows.Input.Key.D7, Keys.D7);
			mappedKeys.Add(System.Windows.Input.Key.D8, Keys.D8);
			mappedKeys.Add(System.Windows.Input.Key.D9, Keys.D9);
			mappedKeys.Add(System.Windows.Input.Key.Decimal, Keys.Decimal);
			mappedKeys.Add(System.Windows.Input.Key.Delete, Keys.Delete);
			mappedKeys.Add(System.Windows.Input.Key.Divide, Keys.Divide);
			mappedKeys.Add(System.Windows.Input.Key.Down, Keys.Down);
			mappedKeys.Add(System.Windows.Input.Key.E, Keys.E);
			mappedKeys.Add(System.Windows.Input.Key.End, Keys.End);
			mappedKeys.Add(System.Windows.Input.Key.Enter, Keys.Enter);
			mappedKeys.Add(System.Windows.Input.Key.Escape, Keys.Escape);
			mappedKeys.Add(System.Windows.Input.Key.F, Keys.F);
			mappedKeys.Add(System.Windows.Input.Key.F1, Keys.F1);
			mappedKeys.Add(System.Windows.Input.Key.F10, Keys.F10);
			mappedKeys.Add(System.Windows.Input.Key.F11, Keys.F11);
			mappedKeys.Add(System.Windows.Input.Key.F12, Keys.F12);
			mappedKeys.Add(System.Windows.Input.Key.F2, Keys.F2);
			mappedKeys.Add(System.Windows.Input.Key.F3, Keys.F3);
			mappedKeys.Add(System.Windows.Input.Key.F4, Keys.F4);
			mappedKeys.Add(System.Windows.Input.Key.F5, Keys.F5);
			mappedKeys.Add(System.Windows.Input.Key.F6, Keys.F6);
			mappedKeys.Add(System.Windows.Input.Key.F7, Keys.F7);
			mappedKeys.Add(System.Windows.Input.Key.F8, Keys.F8);
			mappedKeys.Add(System.Windows.Input.Key.F9, Keys.F9);
			mappedKeys.Add(System.Windows.Input.Key.G, Keys.G);
			mappedKeys.Add(System.Windows.Input.Key.H, Keys.H);
			mappedKeys.Add(System.Windows.Input.Key.Home, Keys.Home);
			mappedKeys.Add(System.Windows.Input.Key.I, Keys.I);
			mappedKeys.Add(System.Windows.Input.Key.Insert, Keys.Insert);
			mappedKeys.Add(System.Windows.Input.Key.J, Keys.J);
			mappedKeys.Add(System.Windows.Input.Key.K, Keys.K);
			mappedKeys.Add(System.Windows.Input.Key.L, Keys.L);
			mappedKeys.Add(System.Windows.Input.Key.Left, Keys.Left);
			mappedKeys.Add(System.Windows.Input.Key.M, Keys.M);
			mappedKeys.Add(System.Windows.Input.Key.Multiply, Keys.Multiply);
			mappedKeys.Add(System.Windows.Input.Key.N, Keys.N);
			mappedKeys.Add(System.Windows.Input.Key.None, Keys.None);
			mappedKeys.Add(System.Windows.Input.Key.NumPad0, Keys.NumPad0);
			mappedKeys.Add(System.Windows.Input.Key.NumPad1, Keys.NumPad1);
			mappedKeys.Add(System.Windows.Input.Key.NumPad2, Keys.NumPad2);
			mappedKeys.Add(System.Windows.Input.Key.NumPad3, Keys.NumPad3);
			mappedKeys.Add(System.Windows.Input.Key.NumPad4, Keys.NumPad4);
			mappedKeys.Add(System.Windows.Input.Key.NumPad5, Keys.NumPad5);
			mappedKeys.Add(System.Windows.Input.Key.NumPad6, Keys.NumPad6);
			mappedKeys.Add(System.Windows.Input.Key.NumPad7, Keys.NumPad7);
			mappedKeys.Add(System.Windows.Input.Key.NumPad8, Keys.NumPad8);
			mappedKeys.Add(System.Windows.Input.Key.NumPad9, Keys.NumPad9);
			mappedKeys.Add(System.Windows.Input.Key.O, Keys.O);
			mappedKeys.Add(System.Windows.Input.Key.P, Keys.P);
			mappedKeys.Add(System.Windows.Input.Key.PageDown, Keys.PageDown);
			mappedKeys.Add(System.Windows.Input.Key.PageUp, Keys.PageUp);
			mappedKeys.Add(System.Windows.Input.Key.Q, Keys.Q);
			mappedKeys.Add(System.Windows.Input.Key.R, Keys.R);
			mappedKeys.Add(System.Windows.Input.Key.Right, Keys.Right);
			mappedKeys.Add(System.Windows.Input.Key.S, Keys.S);
			mappedKeys.Add(System.Windows.Input.Key.Space, Keys.Space);
			mappedKeys.Add(System.Windows.Input.Key.Subtract, Keys.Subtract);
			mappedKeys.Add(System.Windows.Input.Key.T, Keys.T);
			mappedKeys.Add(System.Windows.Input.Key.Tab, Keys.Tab);
			mappedKeys.Add(System.Windows.Input.Key.U, Keys.U);
			mappedKeys.Add(System.Windows.Input.Key.Up, Keys.Up);
			mappedKeys.Add(System.Windows.Input.Key.V, Keys.V);
			mappedKeys.Add(System.Windows.Input.Key.W, Keys.W);
			mappedKeys.Add(System.Windows.Input.Key.X, Keys.X);
			mappedKeys.Add(System.Windows.Input.Key.Y, Keys.Y);
			mappedKeys.Add(System.Windows.Input.Key.Z, Keys.Z);
		}

		internal static void Initialize()
		{
			Application.Current.RootVisual.KeyDown += new KeyEventHandler(RootVisual_KeyDown);
			Application.Current.RootVisual.KeyUp += new KeyEventHandler(RootVisual_KeyUp);

			MapPlatformKeys();
		}

		#region Private Methods
		private static void MapPlatformKeys()
		{
			if (Environment.OSVersion.Platform == PlatformID.MacOSX)
			{
				platformSpecificKeys.Add(30, Keys.OemCloseBrackets);
				platformSpecificKeys.Add(43, Keys.OemComma);
				platformSpecificKeys.Add(27, Keys.OemMinus);
				platformSpecificKeys.Add(33, Keys.OemOpenBrackets);
				platformSpecificKeys.Add(47, Keys.OemPeriod);
				platformSpecificKeys.Add(42, Keys.OemPipe);
				platformSpecificKeys.Add(24, Keys.OemPlus);
				platformSpecificKeys.Add(44, Keys.OemQuestion);
				platformSpecificKeys.Add(39, Keys.OemQuotes);
				platformSpecificKeys.Add(41, Keys.OemSemicolon);
				platformSpecificKeys.Add(50, Keys.OemTilde);
			}
			else
			{
				platformSpecificKeys.Add(221, Keys.OemCloseBrackets);
				platformSpecificKeys.Add(188, Keys.OemComma);
				platformSpecificKeys.Add(189, Keys.OemMinus);
				platformSpecificKeys.Add(219, Keys.OemOpenBrackets);
				platformSpecificKeys.Add(190, Keys.OemPeriod);
				platformSpecificKeys.Add(220, Keys.OemPipe);
				platformSpecificKeys.Add(187, Keys.OemPlus);
				platformSpecificKeys.Add(191, Keys.OemQuestion);
				platformSpecificKeys.Add(222, Keys.OemQuotes);
				platformSpecificKeys.Add(186, Keys.OemSemicolon);
				platformSpecificKeys.Add(192, Keys.OemTilde);
			}
		}

		static void RootVisual_KeyUp(object sender, KeyEventArgs e)
		{
			if (e.Key == Key.Alt)
			{
				pressedKeys.Remove(Keys.LeftAlt);
				pressedKeys.Remove(Keys.RightAlt);
				return;
			}
			if (e.Key == Key.Ctrl)
			{
				pressedKeys.Remove(Keys.LeftControl);
				pressedKeys.Remove(Keys.RightControl);
				return;
			}
			if (e.Key == Key.Shift)
			{
				pressedKeys.Remove(Keys.LeftShift);
				pressedKeys.Remove(Keys.RightShift);
				return;
			}

			if (mappedKeys.ContainsKey(e.Key))
			{
				pressedKeys.Remove(mappedKeys[e.Key]);
			}

			if (platformSpecificKeys.ContainsKey(e.PlatformKeyCode))
			{
				pressedKeys.Remove(platformSpecificKeys[e.PlatformKeyCode]);
			}
		}

		static void RootVisual_KeyDown(object sender, KeyEventArgs e)
		{
			if (e.Key == Key.Alt)
			{
				pressedKeys.Add(Keys.LeftAlt);
				pressedKeys.Add(Keys.RightAlt);
				return;
			}
			if (e.Key == Key.Ctrl)
			{
				pressedKeys.Add(Keys.LeftControl);
				pressedKeys.Add(Keys.RightControl);
				return;
			}
			if (e.Key == Key.Shift)
			{
				pressedKeys.Add(Keys.LeftShift);
				pressedKeys.Add(Keys.RightShift);
				return;
			}

			if (mappedKeys.ContainsKey(e.Key))
			{
				pressedKeys.Add(mappedKeys[e.Key]);
			}

			if (platformSpecificKeys.ContainsKey(e.PlatformKeyCode))
			{
				pressedKeys.Add(platformSpecificKeys[e.PlatformKeyCode]);
			}
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// Returns the current keyboard state.
        /// </summary>
        public static KeyboardState GetState()
        {
			state.pressedKeys = pressedKeys.ToArray();
			return state;
        }

		/// <summary>
		/// Returns the current Chatpad state for the specified player. Reference page contains links to related code samples.
		/// </summary>
		/// <param name="playerIndex">Player index of the Chatpad to query.</param>
		public static KeyboardState GetState(PlayerIndex playerIndex)
		{
			if (playerIndex != PlayerIndex.One)
				throw new InvalidOperationException("Only PlayerIndex.One is supported.");

			return GetState();
		}
        #endregion

		private class KeyComparer : IEqualityComparer<Key>
		{
			public bool Equals(Key x, Key y)
			{
				return x == y;
			}

			public int GetHashCode(Key obj)
			{
				return obj.GetHashCode();
			}
		}
    }
}
